#pragma once

#include <utility>

#include "base.h"
#include "../updater/misc.h"

namespace mdtx::online::indicator
{
    /**
     * @brief Linear decay
     *
     * @tparam T scala type
     */
    template <typename T>
    using lindecay = detail::indi_wrap_cu<updater::lindecay<T>>;

    /**
     * @brief Exponential decay
     *
     * @tparam T scala type
     */
    template <typename T>
    using expdecay = detail::indi_wrap_cu<updater::expdecay<T>>;

    /**
     * @brief 1-period log return
     *
     * @tparam T scala type
     */
    template <typename T>
    using logret = detail::indi_wrap_cu<updater::logret<T>>;

    /**
     * @brief 1-period linear return
     *
     * @tparam T scala type
     */
    template <typename T>
    using linret = detail::indi_wrap_cu<updater::linret<T>>;

    /**
     * @brief 1-period absolute return
     *
     * @tparam T scala type
     */
    template <typename T>
    using absret = detail::indi_wrap_cu<updater::absret<T>>;

    /**
     * @brief 1-period lag
     *
     * @tparam T scala type
     */
    template <typename T>
    class lag1 : public indi_unary1<T>
    {
        using base = indi_unary1<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        T now;
        bool init;

    public:
        tuple_out update(tuple_in const &in) override
        {
            T x = std::get<0>(in), lag;
            if (init)
            {
                lag = std::exchange(now, x);
            }
            else
            {
                lag = now = x;
                init = true;
            }
            return tuple_out(lag);
        }
    };

    /**
     * @brief n-period lag
     *
     * @tparam T scala type
     */
    template <typename T>
    class lagn : public indi_unary1<T>
    {
        using base = indi_unary1<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        boost::circular_buffer<T> buf;

    public:
        explicit lagn(ptrdiff_t period)
            : buf(period)
        {
        }

        tuple_out update(tuple_in const &in) override
        {
            T tmp = buf.front();
            buf.push_back(std::get<0>(in));
            return tuple_out(tmp);
        }
    };
}
